﻿#define  _CRT_SECURE_NO_WARNINGS 1
//数组是⼀组相同类型元素的集合；从这个概念中我们就可以发现2个有价值的信息：
//• 数组中存放的是1个或者多个数据，但是数组元素个数不能为0。
//• 数组中存放的多个数据，类型是相同的。
//数组分为⼀维数组和多维数组，多维数组⼀般⽐较多⻅的是⼆维数组。

// 数组的创建: type arr_name[常量值];
//存放在数组的值被称为数组的元素，数组在创建的时候可以指定数组的⼤⼩和数组的元素类型。
//• type 指定的是数组中存放数据的类型，可以是： char、short、int、float 等，也可以⾃
//定义的类型
//• arr_name 指的是数组名的名字，这个名字根据实际情况，起的有意义就⾏。
//•[] 中的常量值是⽤来指定数组的⼤⼩的，这个数组的⼤⼩是根据实际的需求指定就⾏。

//int main()
//{
//	// 一维数组的创建
//	int math[20]; // 创建了一个数组 用来存放20个人的数学成绩
//	char ch[5]; 
//	double arr[6];
//	return 0;
//}

//有时候，数组在创建的时候，我们需要给定⼀些初始值值，这种就称为初始化的。
//那数组如何初始化呢？数组的初始化⼀般使⽤⼤括号，将数据放在⼤括号中。

//int main()
//{
//	int math[20] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 }; // 完全初始化
//	int math2[20] = { 1,2,3,4,5 }; // 不完全初始化，剩下的元素默认初始化为0
//	char ch[5] = { 'a','b' }; // 不完全初始化 剩下的元素默认初始化为0
//	char ch2[5] = "abc";   // 剩下的元素默认初始化为0
//
//	double arr[6] = { 0.0 }; // 剩下的元素默认初始化为0
//	return 0;
//}

// 数组类型和数组元素类型不一样
//int main()
//{
//	int a = 10;
//	int arr[10] = { 0 };
//	// 数组的元素类型是int  数组的类型是名字去掉  也就是 int[10]
//	return 0;
//}

//C语⾔规定数组是有下标的，下标是从0开始的，假设数组有n个元素，最后⼀个元素的下标是n - 1，下
//标就相当于数组元素的编号   [] - 下标引用操作符  可以访问数组中的元素

#include <stdio.h>
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	//10是指定数组的元素个数
//	printf("%d\n", arr[5]);  // 6
//	// 这里的5是数组元素的下标
//	printf("%d\n", arr[9]);  // 10
//	return 0;
//}

//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };  // 当给数组进行初始化的时候，数组的大小可以省略
//	// 编译器会根据数组的初始化内容自动计算数组的元素个数
//	//10是指定数组的元素个数
//	printf("%d\n", arr[5]);  // 6
//	// 这里的5是数组元素的下标
//	printf("%d\n", arr[9]);  // 10
//	return 0;
//}

// 数组的元素打印  使用循环即可

//for循环
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}

//while循环
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	int i = 0;
//	while (i < 10)
//	{
//		printf("%d ", arr[i]);
//		i++;
//	}
//	return 0;
//}


// 数组的输入
//int main()
//{
//	int arr[10] = { 0 };
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		scanf("%d", &arr[i]);
//	}
//
//	//打印
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}

//数组在内存中是连续存放的。  数组元素需要取地址 数组名不需要取地址 数组名本身就已经是一个地址了

// 一维数组在内容中的存储  x86 是32位的环境  x64是64位的环境
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	// 打印这十个元素的地址
//	for (int i = 0; i < 10; i++)
//	{
//		printf("arr[%d] = %p\n",i, &arr[i]);  // &取地址操作符  地址用%p打印
//	}  // 打印出来发现是连续存放的  都是相差4  这里的地址是16进制的 16进制的数字0 1 2 3 4 5 6 7 8 9 a b c d e f
//	// 一个int类型的值占4个字节  随着数组下标的增长 地址也是由小（低）到大（高）变化的
//	return 0;
//}

//sizeof 中C语⾔是⼀个关键字，是可以计算类型或者变量⼤⼩的，其实 sizeof 也可以计算数组的
//⼤⼩，个数。

//int main()
//{
//	int arr[]= { 1,2,3,4,5,6,7,8,9,10 };
//	printf("%zd\n", sizeof(arr));//  40字节  使用%zd打印就不会报警告
//	printf("%zd\n", sizeof(arr) / sizeof(arr[0]));  // 10个元素
//	return 0;
//}

//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int sz = sizeof(arr) / sizeof(arr[0]);
//	int i = 0;
//	for (i = 0; i < sz; i++)  // 上限变为sz  当我数组发生变化的时候，下面的代码不用改
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}

//int main()
//{
//	int arr[10] = { 0 };  // 10个元素 都是0
//	int arr2[] = { 0 };  // 只有一个元素0
//	int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };  // 不想写个数，只能这样写才能保持数组的元素
//	int arr3[20] = { 1,2,3,4,5,6,7,8,9,10 };  // 20个元素，不完全初始化+
//	return 0;
//}

// 二维数组是把一维数组作为数组元素的数组
// 定义二维数组  type arr_name[常量值1][常量值2];

//例如：
//int arr[3][5];
//解释：上述代码中出现的信息
//• 3表⽰数组有3⾏
//• 5表⽰每⼀⾏有5个元素
//• int 表⽰数组的每个元素是整型类型
//• arr 是数组名，可以根据⾃⼰的需要指定名字

//int main()
//{
//	int data[3][5]; // 三行五列 
//	float score[5][30];
//	return 0;
//}

// 二维数组的初始化
//int main()
//{
//	int data[3][5] = {1,2,3,4,5,6,7,8}; // 不完全初始化  一行排完才到下一行 其他值就是0
//	int data2[3][5] = {0};  //不完全初始化 第一个初始化为0 然后余下默认是0
//	int data3[3][5] = { 1,2,3,4,5 , 2,3,4,5,6 ,3,4,5,6,7 }; // 完全初始化
//	int data4[3][5] = { {1,2},{3,4},{5,6}};  // 按行初始化   分别初始化三个一维数组 所以这6个分数两两都在不同行
//	// 一维数组的初始化是{} 所以可以这样分开初始化
//
//	// 二维数组在初始化的时候 可以省略行，但是不能省略列
//	// 不写列 不能下一行 因为写列可以知道什么时候到下一行 编译器会自己算有几行
//	int arr[][5] = { 1,2,3,4,5,6,7,8 };
//	int arr2[][5] = { {1,2},{3,4},{5,6} };
//	return 0;
//}


// 二维数组的使用 也是通过下标索引 [][]  前面是行下标 后面是列下标  都是从0开始
//int main()
//{
//	int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6 ,3,4,5,6,7 };
//	printf("%d ", arr[2][1]); // 4
//	printf("\n%d ", arr[0][0]); // 1
//	return 0;
//}

//int main()
//{
//	int arr[3][5] = { 0 };
//	// 给二维数组输入值
//	int i = 0;
//	int j = 0;
//	for (i = 0; i < 3; i++)
//	{
//		for (j = 0; j < 5; j++)
//		{
//			scanf("%d", &arr[i][j]);  // 需要取地址 这是数组元素
//		}
//	}
//
//	// 打印二维数组
//	for (i = 0; i < 3; i++)
//	{
//		for (j = 0; j < 5; j++)
//		{
//			printf("%d ", arr[i][j]);
//		}
//		printf("\n");
//	}
//	return 0;
//}



// 二维数组的元素在内存中也是连续存放的
//int main()
//{
//	int arr[3][5] = { 0 };
//  第一行的数组名 arr[0]  第二行数组名arr[1]  第三行数组名arr[2]
//	int i = 0;
//	for (i = 0; i < 3; i++)
//	{
//		for (int j = 0; j < 5; j++)
//		{
//			printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]); 
//		}
//	}
//	return 0;
//}





//在C99标准之前，C语⾔在创建数组的时候，数组⼤⼩的指定只能使⽤常量、常量表达式，或者如果我
//们初始化数据的话，可以省略数组⼤⼩。


//这样的语法限制，让我们创建数组就不够灵活，有时候数组⼤了浪费空间，有时候数组⼜⼩了不够⽤
//的。
//C99中给⼀个变⻓数组（variable - length array，简称 VLA）的新特性，允许我们可以使⽤变量指定
//数组⼤⼩。

// 代码如下：
//int n = a + b;
//int arr[n];


//上⾯⽰例中，数组 arr 就是变⻓数组，因为它的⻓度取决于变量 n 的值，编译器没法事先确定，只
//有运⾏时才能知道 n 是多少。
//变⻓数组的根本特征，就是数组⻓度只有运⾏时才能确定，所以变⻓数组不能初始化。它的好处是程
//序员不必在开发时，随意为数组指定⼀个估计的⻓度，程序可以在运⾏时为数组分配精确的⻓度。有
//⼀个⽐较迷惑的点，变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的，在程序运⾏的时候，根
//据变量的⼤⼩来指定数组的元素个数，⽽不是说数组的⼤⼩是可变的。数组的⼤⼩⼀旦确定就不能再
//变化了。
//遗憾的是在VS2022上，虽然⽀持⼤部分C99的语法，没有⽀持C99中的变⻓数组
//int main()
//{
//	int arr[3][5] = { 1,2,3,4,5 ,2,3,4,5,6 ,3,4,5,6,7 };
//	//1 2 3 4 5
//	//2 3 4 5 6
//	//3 4 5 6 7
//	int i = 0;
//	for (i = 0; i < 5; i++)
//	{
//		int j = 0;
//		for (j = 0; j < 3; j++)
//		{
//			printf("%d ", arr[j][i]);  // 按列来打印
//		}
//		printf("\n");
//	}
//	//1 2 3
//	//2 3 4
//	//3 4 5
//	//4 5 6
//	//5 6 7
//	return 0;
//}



#include <windows.h> // 只能在windows里面用 休眠
#include <string.h>
#include <stdlib.h>  // system
// 练习1：多个字符从两端移动，向中间汇聚
//int main()
//{
//	char arr1[] = "hello bit!!!!!!!!!";  
//	//[hello bit!!!!!!!!!\0]
//	// 0                      下标
//	char arr2[] = "##################";
//	int left = 0;
//	int right = strlen(arr1) - 1; // 这个就是最后一个！的下标
//
//	while (left<=right)  // 中间没有元素了就停下来了
//	{
//		arr2[left] = arr1[left];
//		arr2[right] = arr1[right];
//		printf("%s\n", arr2);
//		Sleep(900); // 休眠900毫秒
//		system("cls");  // 执行清理控制台屏幕的信息
//		left++;
//		right--;
//	}
//	printf("%s\n", arr1);
//	return 0;
//}

//练习2：⼆分查找
//在⼀个升序的数组中查找指定的数字n，很容易想到的⽅法就是遍历数组，但是这种⽅法效率⽐较低。
//⽐如我买了⼀双鞋，你好奇问我多少钱，我说不超过300元。你还是好奇，你想知道到底多少，我就让
//你猜，你会怎么猜？你会1，2，3，4...这样猜吗？显然很慢；⼀般你都会猜中间数字，⽐如：150，然
//后看⼤了还是⼩了，这就是⼆分查找，也叫折半查找。

// 二分查找的前提是数据是有序的，才能使用
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int k = 0;
//	scanf("%d", &k);  // 输入要查找的值，假设是7
//
//	// 首先找出中间元素，把数组分为两部分  首下标加末下标/2 得到的下标对应的元素就是中间元素
//	int sz = sizeof(arr) / sizeof(arr[0]);  // 计算数组的元素个数
//
//	int left = 0;  // 初始首下标
//	int right = sz - 1;  // 末下标
//
//	while (left<=right)
//	{
//		// 中间元素下标
//		int mid = (left + right) / 2;
//		if (arr[mid] < k)
//		{
//			left = mid + 1;
//		}
//		else if (arr[mid] > k)
//		{
//			right = mid - 1;
//		}
//		else
//		{
//			printf("找到了，下标是%d\n", mid);
//			break;
//		}
//	}
//	if (left > right)
//	{
//		printf("找不到\n");
//	}
//	return 0;
//}

// +求中间元素的下标，使⽤ mid = (left + right) / 2 ，如果left和right⽐较⼤的时候可能存在问
// 题，可以使⽤下⾯的⽅式：
// mid = left + (right - left) / 2

// 这样写也可以
//int main()
//{
//	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int k = 0;
//	scanf("%d", &k);  // 输入要查找的值，假设是7
//
//	// 首先找出中间元素，把数组分为两部分  首下标加末下标/2 得到的下标对应的元素就是中间元素
//	int sz = sizeof(arr) / sizeof(arr[0]);  // 计算数组的元素个数
//
//	int left = 0;  // 初始首下标
//	int right = sz - 1;  // 末下标
//	int flag = 0; // 假设找不到
//	while (left <= right)
//	{
//		// 中间元素下标
//		int mid = (left + right) / 2;
//		if (arr[mid] < k)
//		{
//			left = mid + 1;
//		}
//		else if (arr[mid] > k)
//		{
//			right = mid - 1;
//		}
//		else
//		{
//			printf("找到了，下标是%d\n", mid);
//			flag = 1;
//			break;
//		}
//	}
//	if (flag == 0)
//	{
//		printf("找不到\n");
//	}
//	return 0;
//}













	